package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ListServiceImpl implements ListService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private GoodsDao goodsDao;

    @Autowired
    private ProductFeignClient productFeignClient;

    //上架    商品 sku  到ES索引库
    @Override
    public void onSale(Long skuId) {
        Goods goods = new Goods();
        //补全所有的数据
        //查询 sku_info数据
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        //ID
        goods.setId(skuInfo.getId());
        //图片
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
        //标题
        goods.setTitle(skuInfo.getSkuName());
        //价格
        goods.setPrice(skuInfo.getPrice().doubleValue());
        ////////////////////////////////////////////////////
        //时间
        goods.setCreateTime(new Date());
        ///////////////////////////////////////////////////
        //查询 base_trademark
        BaseTrademark baseTrademark = productFeignClient.getBaseTrademark(skuInfo.getTmId());
        //品牌ID
        goods.setTmId(baseTrademark.getId());
        //品牌名称
        goods.setTmName(baseTrademark.getTmName());
        //品牌的Logo
        goods.setTmLogoUrl(baseTrademark.getLogoUrl());
        ////////////////////////////////////////////////////
        //查询一二三级分类 视图 base_category_view
        BaseCategoryView baseCategoryView = productFeignClient.getBaseCategoryView(skuInfo.getCategory3Id());
        goods.setCategory1Id(baseCategoryView.getCategory1Id());
        goods.setCategory1Name(baseCategoryView.getCategory1Name());
        goods.setCategory2Id(baseCategoryView.getCategory2Id());
        goods.setCategory2Name(baseCategoryView.getCategory2Name());
        goods.setCategory3Id(baseCategoryView.getCategory3Id());
        goods.setCategory3Name(baseCategoryView.getCategory3Name());
        //查询平台属性表  （3张表）
        List<SkuAttrValue> baseAttrInfoAndBaseAttrValue = productFeignClient.getBaseAttrInfoAndBaseAttrValue(skuId);

        List<SearchAttr> attrs = baseAttrInfoAndBaseAttrValue.stream().map((skuAttrValue) -> {
            SearchAttr searchAttr = new SearchAttr();
            //平台属性ID
            searchAttr.setAttrId(skuAttrValue.getBaseAttrInfo().getId());
            //平台属性名称
            searchAttr.setAttrName(skuAttrValue.getBaseAttrInfo().getAttrName());
            //平台属性值名称
            searchAttr.setAttrValue(skuAttrValue.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(attrs);
        //添加 、修改 根据ID 存在了修改  不存在就是添加
        goodsDao.save(goods);
    }

    //下架   从ES库存中删除索引
    @Override
    public void cancelSale(Long skuId) {
        //删除
        goodsDao.deleteById(skuId);
    }

    //更新热度
    @Override
    public void hotScore(Long skuId) {
        //1:更新Redis 缓存   （五大数据类型）
        String name = "hotScore";
        // ZSet  原子性操作 不需要再上锁了
        Double score = redisTemplate.opsForZSet().incrementScore(name, skuId, 1);
        //2:达到一定数量 才更新ES索引库
        if (score % 10 == 0) {//0-10 11-20
            Optional<Goods> byId = goodsDao.findById(skuId);
            Goods goods = byId.get();
            goods.setHotScore(Math.round(score));
            goodsDao.save(goods);
        }
    }

    @Override
    public SearchResponseVo list(SearchParam searchParam) {
        // 1：构建搜索请求对象
        SearchRequest searchRequest = buildSearchRequest(searchParam);
        // 2：执行搜索
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 3：解析结果
        SearchResponseVo vo = parseSearchResponse(searchResponse);
        vo.setPageNo(searchParam.getPageNo());
        vo.setPageSize(searchParam.getPageSize());
        vo.setTotalPages((vo.getTotal() + vo.getPageSize() - 1) / vo.getPageSize());
        return vo;
    }

    // 3：解析结果
    private SearchResponseVo parseSearchResponse(SearchResponse searchResponse) {
        SearchResponseVo vo = new SearchResponseVo();
        // 1、总条数
        SearchHits hits = searchResponse.getHits();
        long totalHits = hits.getTotalHits();
        System.out.println("总条数：" + totalHits);
        vo.setTotal(totalHits);
        // 2、商品数据
        SearchHit[] hits1 = hits.getHits();
        List<Goods> goodsList = Arrays.stream(hits1).map((hit) -> {
            String sourceAsString = hit.getSourceAsString();
            Goods goods = JSONObject.parseObject(sourceAsString, Goods.class);
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null){
                HighlightField title = highlightFields.get("title");
                if (title != null){
                    String string = title.fragments()[0].toString();
                    System.out.println(string);
                    goods.setTitle(string);
                }
            }
            return goods;
        }).collect(Collectors.toList());
        vo.setGoodsList(goodsList);
        // 3、解析品牌集合
        Map<String, Aggregation> stringAggregationMap = searchResponse.getAggregations().asMap();
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) stringAggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> tmVoList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            //品牌ID
            tmVo.setTmId(Long.parseLong(bucket.getKeyAsString()));
            //品牌名称
            ParsedStringTerms tmNameAgg = ((Terms.Bucket) bucket).getAggregations().get("tmNameAgg");
            tmVo.setTmName(tmNameAgg.getBuckets().get(0).getKeyAsString());
            //品牌Logo
            ParsedStringTerms tmLogoUrlAgg = ((Terms.Bucket) bucket).getAggregations().get("tmLogoUrlAgg");
            tmVo.setTmLogoUrl(tmLogoUrlAgg.getBuckets().get(0).getKeyAsString());
            return tmVo;
        }).collect(Collectors.toList());
        vo.setTrademarkList(tmVoList);
        return vo;
    }

    // 1：构建搜索请求对象
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        // 创建请求对象 并指定索引库
        SearchRequest searchRequest = new SearchRequest("goods");
        //类型  当前版本6.8.1   type还在  7.0以上 type 消失了  一个库只对应一个索引表
        //searchRequest.types("info");
        // 构建搜索条件对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 创建组合条件对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 开始向组合条件对象中设置条件
        // 1、关键词
        String keyword = searchParam.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            //must : 必须   where title like %苹果% and title like %手机%
            //mustNot : 不必须   where title like %苹果% not title like %手机%
            //should : 应该   where title like %苹果% OR title like %手机%
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword).operator(Operator.AND));
        } else {
            // 如果搜索词为空则查询全部，但是比较耗费性能，一般不让走到这一步
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        // 2、过滤品牌
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            // 由于前端传过来的 trademark 是  品牌ID:品牌的名称 的拼接字符串，所以在这里要就行截取
            String[] split = StringUtils.split(trademark, ":");
            // filter底层和must一样，都表示and关系，但是filter在这里更有语义，表示过滤
            // matchQuery匹配查询，相当于like，且会对查询的词进行分词
            // termQuery相当于==，且不分词
            boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", split[0]));
        }
        // 3、一二三级分类条件过滤
        Long category1Id = searchParam.getCategory1Id();
        if (null != category1Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", category1Id));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (null != category2Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", category2Id));
        }
        Long category3Id = searchParam.getCategory3Id();
        if (null != category3Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", category3Id));
        }
        // 4、过滤平台属性
        String[] props = searchParam.getProps();
        if (null != props && props.length > 0) {
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            for (String prop : props) {
                BoolQueryBuilder sunBoolQuery = QueryBuilders.boolQuery();
                //prop   是   平台属性ID:平台属性值名称:平台属性名称     拼接出来的字符串
                String[] p = prop.split(":");
                sunBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", p[0]));
                sunBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", p[1]));
                boolQuery.must(QueryBuilders.nestedQuery("attrs", sunBoolQuery, ScoreMode.None));
            }
            boolQueryBuilder.filter(boolQuery);
        }
        // 将组合条件对象设置到搜索条件对象
        searchSourceBuilder.query(boolQueryBuilder);
        // 5、排序 order   1:asc ..
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] o = order.split(":");
            if (o != null && o.length == 2) {
                String s = "";
                switch (o[0]) {
                    case "1":
                        s = "hotScore";
                        break;
                    case "2":
                        s = "price";
                        break;
                }
                searchSourceBuilder.sort(s, o[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC);
            }
        } else {
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);
        }
        // 6、分页   limit 开始行,每页数
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        searchSourceBuilder.from((pageNo - 1) * pageSize);
        searchSourceBuilder.size(pageSize);
        // 7、搜索词高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<font color='red'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);
        // 8：根据关键词 查询 品牌集合  8个数据  ： 华为2  小米1  苹果5
        // 华为 小米  苹果    group by  ==  ES 索引库 聚合 select max(id) as idddd from
        // 品牌ID 品牌名称 品牌Logo
        searchSourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl")));
        // 查看一下DSL语句
        System.out.println(searchSourceBuilder.toString());
        // 将构建的搜索条件对象设置到请求对象中
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

}
